package org.simple.web.jwt.config;

import org.simple.web.jwt.filter.JwtAuthenticationTokenFilter;
import org.simple.web.jwt.filter.UserLoginFilter;
import org.simple.web.jwt.filter.UserRefreshFilter;
import org.simple.web.jwt.handler.SimpleAuthenticatingFailureHandler;
import org.simple.web.jwt.handler.SimpleAuthenticatingSuccessHandler;
import org.simple.web.jwt.property.JwtAuthFilterProperty;
import org.simple.web.jwt.property.SimpleSecurityProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

/**
 * 项目名称：web-web-jwt
 * 类名称：WebSecurityConfig
 * 类描述：WebSecurityConfig Spring Security 配置
 * 创建时间：2018/4/11 16:48
 *
 * @author sisyphus   (E-mail:1620657419@qq.com)
 * @version v1.0
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan(basePackages = {"org.simple.web.jwt"})
public class WebSecurityConfig {

    @Autowired
    private SimpleSecurityProperty simpleSecurityProperty;

    @Autowired
    private SimpleAuthenticatingSuccessHandler simpleAuthenticatingSuccessHandler;

    @Autowired
    private SimpleAuthenticatingFailureHandler simpleAuthenticatingFailureHandler;

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Bean
    public AuthenticationManager getManagerBean(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
        // 由于使用的是JWT，我们这里不需要csrf
        http.csrf(AbstractHttpConfigurer::disable);
        //未授权处理
        http.exceptionHandling(httpSecurityExceptionHandlingConfigurer -> httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint((request, response, authException) -> {
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setStatus(401);
        }));
        // 基于token，所以不需要session
        http.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        http.authorizeHttpRequests(authorizeRequests -> {
            JwtAuthFilterProperty filter = simpleSecurityProperty.getFilter();
            String[] urls = filter.getExceptUrl().split(",");
            for (String url : urls) {
                if (url != null && !"".equals(url)) {
                    // 对于获取token的rest api要允许匿名访问
                    authorizeRequests.requestMatchers(new AntPathRequestMatcher(url)).permitAll();
                }
            }

            if (filter.getIsIgnoreSwagger()) {
                authorizeRequests.requestMatchers(new AntPathRequestMatcher("/swagger-resources/**")).permitAll();
                authorizeRequests.requestMatchers(new AntPathRequestMatcher("/webjars/**")).permitAll();
                authorizeRequests.requestMatchers(new AntPathRequestMatcher("/swagger-ui.html/**")).permitAll();
                authorizeRequests.requestMatchers(new AntPathRequestMatcher("/v2/api-docs")).permitAll();
            }

            authorizeRequests.requestMatchers(new AntPathRequestMatcher("/**", HttpMethod.OPTIONS.name())).permitAll();
            // 除上面外的所有请求全部需要鉴权认证
            authorizeRequests.anyRequest().authenticated();
        });

        // 添加JWT filter
        //将token验证添加在密码验证前面
        http.addFilterBefore(getJwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(getUserLoginFilter(authenticationManager), JwtAuthenticationTokenFilter.class);
        http.addFilterBefore(getUserRefreshFilter(authenticationManager), UserLoginFilter.class);
        // 禁用缓存
        http.headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer.cacheControl(HeadersConfigurer.CacheControlConfig::disable));
        return http.build();
    }

    @Bean
    public JwtAuthenticationTokenFilter getJwtAuthenticationTokenFilter() throws Exception {
        return new JwtAuthenticationTokenFilter();
    }

    @Bean
    public UserLoginFilter getUserLoginFilter(AuthenticationManager authenticationManager) throws Exception {
        UserLoginFilter userLoginFilter = new UserLoginFilter(authenticationManager);
        userLoginFilter.setAuthenticationSuccessHandler(simpleAuthenticatingSuccessHandler);
        userLoginFilter.setAuthenticationFailureHandler(simpleAuthenticatingFailureHandler);
        return userLoginFilter;
    }

    @Bean
    public UserRefreshFilter getUserRefreshFilter(AuthenticationManager authenticationManager) throws Exception {
        UserRefreshFilter userRefreshFilter = new UserRefreshFilter(authenticationManager);
        userRefreshFilter.setAuthenticationSuccessHandler(simpleAuthenticatingSuccessHandler);
        userRefreshFilter.setAuthenticationFailureHandler(simpleAuthenticatingFailureHandler);
        return userRefreshFilter;
    }

}
